package main

import (
	"fmt"
)

// 结构体也是一种类型
type person struct {
	name string
	age  uint
	addr address
}
type address struct {
	province string
	city     string
}

type Stringer interface {
	String() string
}

/*
	注意：如果一个接口有多个方法，那么需要实现接口的每个方法才算是实现了这个接口。
	实现接口对应类型 person 和 *person
*/
func (p person) String() string {
	return fmt.Sprintf("the name is %s,age is %d", p.name, p.age)
}

/*	todo 一般指针类型为主
	以指针类型接收者实现接口的时候，只有对应的指针类型才被认为实现了该接口。
	实现接口对应类型*person
	如果外部类型定义了和内部类型同样的方法，
	那么外部类型的会覆盖内部类型，这就是方法的覆写。
	方法覆写不会影响内部类型的方法实现。
*/
// func (p *person) String() string {
// 	return fmt.Sprintf("the name is %s,age is %d", p.name, p.age)
// }

// address
func (addr address) String() string {
	return fmt.Sprintf("the addr is %s%s", addr.province, addr.city)
}

// person实现了Stringer 接口
func printString(s fmt.Stringer) {
	fmt.Println(s.String())
}

/*
	工厂函数一般用于创建自定义结构体
*/
func NewPerson(name string) *person {
	return &person{name: name}
}

//工厂函数，返回一个error接口，其实具体实现是*errorString
func New(text string) error {
	return &errorString{text}
}

//结构体，内部一个字段s，存储错误信息
type errorString struct {
	s string
}

//用于实现error接口
func (e *errorString) Error() string {
	return e.s
}

/*
	go没有继承的概念 提倡组合
*/

func main() {
	// var p person
	// p:=person{"lt",29}
	// p:=person{age:30,name:"飞雪无情"}
	// fmt.Println(p.name,p.age)
	// p1:=person{
	//     age:30,
	//     name:"飞雪无情",
	//     addr:address{
	//         province: "北京",
	//         city:     "北京",
	//     },
	// }
	// fmt.Println(p1.addr.province)

	// printString(&p) // 指针类型也是可以的
	// printString(p1.addr)

	// p3 := NewPerson("lt")
	// printString(p3)

	// 类型断言
	// var s fmt.Stringer
	// p1:=person{age:30,name:"飞雪无情"}
	// s=p1
	// p2:=s.(person)
	// fmt.Println(p2)

	// a:=s.(address)
	// fmt.Println(a)
	// 类型断言多值返回
	// a, ok := s.(address)
	// if ok {
	// 	fmt.Println(a)
	// } else {
	// 	fmt.Println("s不是一个address")
	// }

	p := &person{age: 30, name: "lt"} // 指针类型
	var w WalkRun
	w = p
	w.Walk()
	w.Run()

}

/*
	接口实现方法
*/
type WalkRun interface {
	Walk()
	Run()
}

func (p *person) Walk() {
	fmt.Printf("%s能走\n", p.name)
}
func (p *person) Run() {
	fmt.Printf("%s能跑\n", p.name)
}
